package com.youxin.chat.basic.service.impl;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ipi.cloud.interfaces.dto.ErrorInfo;
import com.ipi.cloud.sms.access.request.CloudSmsCore;
import com.ipi.cloud.sms.access.vo.SmsAccount;
import com.ipi.cloud.sms.access.vo.response.SmsBatchSumbitResponse;
import com.youxin.base.BaseResultCode;
import com.youxin.base.TPage;
import com.youxin.chat.basic.config.YouxinProperties;
import com.youxin.chat.basic.dto.SmsConfigDto;
import com.youxin.chat.basic.dto.constant.SmsTypeEnum;
import com.youxin.chat.basic.dto.constant.SysConfigConstant;
import com.youxin.chat.basic.dto.sms.SmsDto;
import com.youxin.chat.basic.dto.sms.SmsListDto;
import com.youxin.chat.basic.dto.sms.SmsReqDto;
import com.youxin.chat.basic.mapper.SmsMapper;
import com.youxin.chat.basic.model.Sms;
import com.youxin.chat.basic.service.SmsService;
import com.youxin.chat.basic.service.SysConfigService;
import com.youxin.common.constant.RedisKey;
import com.youxin.exception.SystemException;
import com.youxin.utils.IPUtil;
import com.youxin.utils.RandomUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


@Service
public class SmsServiceImpl extends ServiceImpl<SmsMapper, Sms> implements SmsService {
    private Logger logger = LoggerFactory.getLogger(this.getClass());


    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private YouxinProperties youxinConfig;

    @Resource
    private SysConfigService sysConfigService;

    int PERIOD = 180;

    private ExecutorService executor = new ThreadPoolExecutor(10, 20,
            60L, TimeUnit.SECONDS,
            new ArrayBlockingQueue(500));

    @Override
    public void sendSms(Integer type, String phone, String content, Long ip) {
        logger.info("请求短信验证码接口，type={},phone={},content={},ip={}", type, phone, content, ip);
        checkSendSms(phone, type, ip);
        String validateCode = RandomUtil.randomStr(6, RandomUtil.NUM_CHAR);
        content = String.format(content, validateCode);

        executor.execute(new SendSmsRunner(content, phone));

        String key = RedisKey.SMS_CODE_PREFIX + phone + "." + type;
        stringRedisTemplate.opsForValue().set(key, validateCode, PERIOD, TimeUnit.SECONDS);
        saveSms(type, phone, content, ip);
    }

    @Override
    public TPage<SmsDto> getSmsList(SmsListDto smsListDto) {
        IPage<SmsDto> page = this.baseMapper.getSmsList( new Page<SmsDto>(smsListDto.getCurrentPage(), smsListDto.getShowCount()),smsListDto);
        List<SmsDto> list = page.getRecords();
        list.stream().forEach(item -> {
            item.setIp(IPUtil.longToIP(Long.valueOf(item.getIp())));
        });

        return TPage.page(list, page.getTotal());
    }


    private void checkSendSms(String phone, Integer type, Long ip) {
        SmsTypeEnum smsTypeEnum = SmsTypeEnum.forType(type);
        String str = sysConfigService.getConfigValue(SysConfigConstant.SMS_TYPE_INFO);
        Map<Integer, Integer> countMap = new HashMap<>();
        if (!StringUtils.isEmpty(str)) {
            JSONArray jsonArray = JSONObject.parseArray(str);
            Iterator iterator = jsonArray.iterator();
            for (int i = 0; i < jsonArray.size(); i++) {
                JSONObject jsonObject = jsonArray.getJSONObject(i);
                countMap.put(jsonObject.getInteger("type"), jsonObject.getInteger("maxCount"));
            }
        }
        if (smsTypeEnum == null) {
            logger.error("请求短信验证码异常，参数不对，phone={},type={},ip={}", phone, type, ip);
            throw new SystemException(BaseResultCode.COMMON_FAIL, "验证码类型有误");
        }
        int count = count(
                new LambdaQueryWrapper<Sms>().eq(Sms::getMobile, phone)
                        .ge(Sms::getCreateTime, LocalDate.now().atStartOfDay()));
        int maxCount = Optional.ofNullable(countMap.get(type)).orElse(smsTypeEnum.getMaxCount());
        if (count >= maxCount) {
            logger.error("手机号超出当日最大限制，maxCount={},type={}，phone={}", smsTypeEnum.getMaxCount(), smsTypeEnum.getDesc(), phone);
            throw new SystemException(BaseResultCode.EXCEED_LIMIT, "短信超出当日最大条数限制");
        }
        int ipCount =count(
                new LambdaQueryWrapper<Sms>().eq(Sms::getIp, ip)
                        .ge(Sms::getCreateTime, LocalDate.now().atStartOfDay()));
        if (ipCount >= 100) {
            logger.error("IP超出当日最大限制，maxCount={},type={}，ip={}", smsTypeEnum.getMaxCount(), smsTypeEnum.getDesc(), ip);
            throw new SystemException(BaseResultCode.EXCEED_LIMIT, "ip超出当日最大条数限制");
        }
        String key = RedisKey.SMS_CODE_PREFIX + phone + "." + type;
        if (stringRedisTemplate.opsForValue().get(key) != null) {
            logger.error("验证码已发送，请勿重复发送，type={}，phone={}", smsTypeEnum.getDesc(), phone);
            throw new SystemException(BaseResultCode.REQUEST_FREQUENT, "验证码已发送，请勿重复发送");
        }
    }

    private String doRegisterCode(String validateCode, SmsReqDto smsReqDto) {
        String template = "您的短信验证码为:%s,验证码%s分钟内有效,，此验证码勿提供给他人，请勿转发！";
        String content = String.format(template, validateCode, PERIOD / 60);
        return content;
    }

    private String doLoginCode(String validateCode, SmsReqDto smsReqDto) {
        String template = "您的短信验证码为:%s,验证码%s分钟内有效,，此验证码勿提供给他人，请勿转发！";
        String content = String.format(template, validateCode, PERIOD / 60);

        return content;
    }

    class SendSmsRunner implements Runnable{
        private String content;
        private String phone;

        public SendSmsRunner(String content, String phone) {
            this.content = content;
            this.phone = phone;
        }

        @Override
        public void run() {
            logger.info("发送短信验证码，phone ={},content={}", content, phone);
            String smsConfigStr = sysConfigService.getConfigValue(SysConfigConstant.SMS_CONFIG_INFO);
            if (StringUtils.isEmpty(smsConfigStr)) {
                smsConfigStr = JSONObject.toJSONString(youxinConfig.getSms());
            }
            SmsConfigDto smsConfigDto = JSONObject.parseObject(smsConfigStr, SmsConfigDto.class);
            String entNo = Optional.ofNullable(smsConfigDto.getEntNo()).orElse(youxinConfig.getSms().getEntNo());
            String url = Optional.ofNullable(smsConfigDto.getUrl()).orElse(youxinConfig.getSms().getUrl());
            String account = Optional.ofNullable(smsConfigDto.getAccount()).orElse(youxinConfig.getSms().getAccount());
            String password = Optional.ofNullable(smsConfigDto.getPassword()).orElse(youxinConfig.getSms().getPassword());

            /***调用接口进行请求批量发送  ***/
            SmsBatchSumbitResponse resp = CloudSmsCore.getInstance().batchSendSms(
                    url, new SmsAccount(entNo,
                            account, password), phone, content);
            if (resp.isSuccess()) {// 提交成功
                //SmsBatchSmbitResult result = resp.getBatchSmbitResult();
                logger.info("cmd=SmsUtil:sendMore msg=短信提交成功, phone=" + phone + ", context=" + content);
            } else { // 提交返回错误
                ErrorInfo errorInfo = resp.getErrorInfo();
                logger.error("cmd=SmsUtil:sendMore msg=短信提交失败：phone=" + phone + ", context=" + content + " errorMsg=" + JSON.toJSONString(errorInfo));
                throw new SystemException(BaseResultCode.INTERNAL_ERROR, "发送短信验证码失败");
            }
            logger.info("发送短信验证码完成");
        }
    }

    private void saveSms(Integer type, String phone, String content, Long ip) {
        Sms sms = new Sms();
        sms.setIp(ip);
        sms.setSmsType(type);
        sms.setSmsContent(content);
        sms.setCreateTime(LocalDateTime.now());
        sms.setMobile(phone);
        save(sms);
    }
}
